Skip to content

fix(7696): scrollable settings popup on short viewports#7703

Merged
JohnMcLear merged 3 commits intodevelopfrom
fix/7696-settings-popup-scroll
May 7, 2026
Merged

fix(7696): scrollable settings popup on short viewports#7703
JohnMcLear merged 3 commits intodevelopfrom
fix/7696-settings-popup-scroll

Conversation

@JohnMcLear
Copy link
Copy Markdown
Member

@JohnMcLear JohnMcLear commented May 7, 2026

Summary

  • Settings popup (especially with Pad-wide Settings enabled) currently crops items off-screen with no scrollbar when the viewport is short — the bug reported in Pad-wide Settings get cropped if UI too small #7696.
  • Move max-height + overflow-y:auto from the mobile-only (max-width:800px) media query onto the base .popup-content rule so all popups stay scrollable at any viewport width. The chat panel (#users) keeps overflow:visible so the colour-picker can float outside it.
  • Adds a Playwright regression test that opens Settings at 900×500, asserts the popup is a scroll container, and confirms the bottom-most "Delete pad" button is reachable via scroll.

Fixes #7696

Test plan

  • pnpm run ts-check — clean for changed files (pre-existing errors are all under plugin_packages/)
  • CI runs the new Playwright test settings popup stays scrollable when the viewport is short
  • Manual: shrink browser window to ~1/3 screen, open Settings → scrollbar appears, all items reachable, "Delete pad" still clickable

🤖 Generated with Claude Code

Move max-height + overflow:auto out of the mobile-only media query and
onto the base .popup-content rule so the Settings popup (and other
popups) gain a scrollbar instead of cropping items off-screen when the
window is short. Pad-wide Settings is the worst offender because it
adds a second column of controls plus a Delete pad button.

Adds a Playwright regression test that verifies the popup is scrollable
and the Delete pad button is reachable at a 900x500 viewport.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@qodo-code-review
Copy link
Copy Markdown

ⓘ You've reached your Qodo monthly free-tier limit. Reviews pause until next month — upgrade your plan to continue now, or link your paid account if you already have one.

@qodo-free-for-open-source-projects
Copy link
Copy Markdown

Review Summary by Qodo

Fix settings popup scrollability on short viewports with regression test

🐞 Bug fix 🧪 Tests

Grey Divider

Walkthroughs

Description
• Fix settings popup cropping items off-screen on short viewports
• Move scrolling styles from mobile-only media query to base popup
• Add Playwright regression test for popup scrollability at 900×500
• Preserve chat panel's overflow behavior for color-picker floats
Diagram
flowchart LR
  A["Short viewport<br/>900×500"] -->|"Previously cropped<br/>items off-screen"| B["Settings popup<br/>overflow hidden"]
  A -->|"After fix:<br/>scrollable"| C["Popup scrolls<br/>Delete pad reachable"]
  D["CSS changes"] -->|"Move max-height<br/>+ overflow-y:auto"| E["Base .popup-content<br/>rule"]
  D -->|"Preserve chat<br/>overflow:visible"| F["#users .popup-content<br/>exception"]
  G["Playwright test"] -->|"Verifies scrollability<br/>at 900×500"| H["Regression coverage"]
Loading

Grey Divider

File Changes

1. src/tests/frontend-new/specs/pad_settings.spec.ts 🧪 Tests +23/-0

Add regression test for popup scrollability

• Added new Playwright test settings popup stays scrollable when the viewport is short
• Test sets viewport to 900×500 and verifies popup has overflow-y: auto
• Confirms popup scrollHeight exceeds clientHeight and Delete pad button is reachable via scroll
• Addresses regression coverage for issue #7696

src/tests/frontend-new/specs/pad_settings.spec.ts


2. src/static/css/pad/popup.css 🐞 Bug fix +11/-4

Enable popup scrolling for all viewport widths

• Move max-height: calc(100vh - 20px) and overflow-y: auto from mobile-only media query to base
 .popup-content rule
• Apply scrolling behavior to all viewports, not just mobile (max-width: 800px)
• Add exception rule .popup#users .popup-content { overflow: visible } to preserve chat panel's
 color-picker float behavior
• Remove duplicate overflow rules from mobile media query

src/static/css/pad/popup.css


Grey Divider

Qodo Logo

@qodo-free-for-open-source-projects
Copy link
Copy Markdown

qodo-free-for-open-source-projects Bot commented May 7, 2026

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider


Action required

1. Dropdowns clipped in popups ✓ Resolved 🐞 Bug ≡ Correctness
Description
Setting .popup-content to overflow-y:auto makes it a scroll container, which clips absolutely
positioned descendants such as Nice Select’s option list. As a result, font/language dropdown menus
in Settings can be truncated when opened near the bottom of the popup on short viewports, making
options hard/impossible to click.
Code

src/static/css/pad/popup.css[R33-37]

+  /* Constrain height so popups (notably Settings with Pad-wide Settings
+     enabled) scroll instead of cropping items off-screen on short windows.
+     Fixes #7696. */
+  max-height: calc(100vh - 20px);
+  overflow-y: auto;
Evidence
The PR makes .popup-content a vertical scroll container (overflow-y:auto), which clips
overflowing content. Nice Select renders its dropdown list as an absolutely positioned element below
the control (position:absolute; top:100%), so when the list extends beyond the scroll container
bounds it will be visually clipped. Settings contains Nice Select dropdowns (font and language), so
this can directly impact Settings usability on short viewports—the exact scenario this PR targets.

src/static/css/pad/popup.css[25-44]
src/static/css/pad/form.css[106-139]
src/static/js/vendors/nice-select.ts[96-125]
src/templates/pad.html[129-199]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
With `overflow-y: auto` on `.popup-content`, popups become scroll containers that clip absolutely-positioned dropdown lists (Nice Select). This can truncate the font/language dropdown list in Settings and other popups on short viewports.
### Issue Context
Nice Select renders the options list as `position: absolute; top: 100%` under the `.nice-select` element. When `.popup-content` is a scroll container, overflow outside the popup is clipped.
### Fix Focus Areas
- Make Nice Select lists escape the scroll container when used inside popups (e.g., switch to `position: fixed` and set coordinates like the toolbar path does, or move/portal the list to `body`).
- Alternatively, toggle a CSS class on the nearest `.popup-content` while a Nice Select is open to temporarily set overflow to visible, then restore it on close.
- src/static/css/pad/popup.css[25-44]
- src/static/css/pad/form.css[106-139]
- src/static/js/vendors/nice-select.ts[96-129]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Brittle scrollHeight test assertion ✓ Resolved 🐞 Bug ☼ Reliability
Description
The new Playwright test hard-requires scrollHeight > clientHeight, so it will fail if Settings
content ever fits within 900×500 (due to CSS tweaks, localization, or feature changes) even though
the popup remains correctly scrollable. The test already checks reachability of the bottom button
via scrollIntoViewIfNeeded() + toBeInViewport(), which is the behavior that matters.
Code

src/tests/frontend-new/specs/pad_settings.spec.ts[R182-187]

+    const dims = await popupContent.evaluate((el) => ({
+      overflowY: getComputedStyle(el).overflowY,
+      scrolls: el.scrollHeight > el.clientHeight,
+    }));
+    expect(dims.overflowY).toBe('auto');
+    expect(dims.scrolls).toBe(true);
Evidence
The test asserts runtime layout overflow (scrollHeight > clientHeight) rather than just verifying
that scrolling works and that the bottom control is reachable. This makes the test fail for benign
UI/layout changes that reduce content height, even when the intended regression (cropped unreachable
controls) is fixed.

src/tests/frontend-new/specs/pad_settings.spec.ts[170-191]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The test requires `scrollHeight > clientHeight`, which is fragile because it depends on Settings content height always overflowing at 900×500.
### Issue Context
The key behavior to validate is: the popup uses scrollable overflow styling and the bottom-most action (Delete pad) is reachable (not permanently cropped). The test already scrolls to `#delete-pad` and asserts it is in the viewport.
### Fix Focus Areas
- Remove/relax the `scrollHeight > clientHeight` assertion and rely on reachability checks.
- Or, if overflow must be asserted, first assert `#delete-pad` is NOT in viewport before scrolling (to prove scrolling was needed) and adjust viewport height accordingly.
- src/tests/frontend-new/specs/pad_settings.spec.ts[170-191]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment thread src/static/css/pad/popup.css
Qodo flagged that making .popup-content a scroll container clips
absolutely-positioned descendants — so the Settings popup's font and
language dropdowns can be truncated when their list extends past the
popup's scroll bounds on short viewports.

Mirror the existing toolbar workaround: when a nice-select sits inside
.popup-content, switch the list to position:fixed (CSS) and place it
with viewport-relative coordinates from getBoundingClientRect (JS),
respecting the existing reverse class for upward-opening lists.

Also relax the regression test per Qodo: drop the brittle
scrollHeight > clientHeight assertion in favour of asserting the
popup declares overflow-y:auto and proving Delete pad is initially
off-screen, then reachable via scroll.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@JohnMcLear
Copy link
Copy Markdown
Member Author

Thanks @qodo-free-for-open-source-projects — both addressed in 26cebda.

  1. Dropdowns clipped in popups — Mirrored the toolbar workaround for popup-contained nice-selects: the list switches to position: fixed (form.css) and nice-select.ts sets viewport-relative top/left/min-width from getBoundingClientRect(), picking the anchor side based on the existing .reverse class so upward-opening lists still work. Toolbar path is untouched.

  2. Brittle scrollHeight assertion — Dropped scrollHeight > clientHeight. The test now asserts the popup declares overflow-y: auto and proves the bug is fixed by checking #delete-pad is initially not in viewport, then reachable after scrollIntoViewIfNeeded().

When a nice-select inside a popup-content scroll container sits in the
lower half of the viewport, the JS adds the .reverse class so the list
opens upward. The default .reverse rule sets bottom: calc(100% + 5px),
which is fine when the list is position:absolute relative to its parent
— but with the position:fixed treatment the popup branch uses, that
percentage resolves against the viewport and pushes the list ~100vh
above the screen, so it appears not to open at all until you scroll to
the bottom of the popup (where .reverse no longer triggers).

Override the rule for both .toolbar and .popup so .reverse drops back to
bottom: auto and JS-set `top` controls placement, with a JS belt-and-
braces also setting `bottom: auto` inline.

Adds a Playwright regression test that scrolls the settings popup to
the bottom, opens the Pad-wide font dropdown, and asserts the list is
both visible and inside the viewport.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@JohnMcLear JohnMcLear merged commit afee796 into develop May 7, 2026
53 of 55 checks passed
@JohnMcLear JohnMcLear deleted the fix/7696-settings-popup-scroll branch May 7, 2026 19:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Pad-wide Settings get cropped if UI too small

1 participant